package aclInfoCollector.utils.classLoader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Hashtable;

/**
 * La classe ClassLoaderACL permet de charger un fichier .class dans la machine
 * virtuelle.
 * 
 * @version 1.0
 *@author Bendavid Olivier
 *@author Haderer Nicolas
 */

public class ClassLoaderACL extends ClassLoader {

	Hashtable<String, Class<?>> cache = new Hashtable<String, Class<?>>();
	byte data[];

	/**
	 * lit et charge le byte-code d'un fichier .class dans l'attribut data
	 * 
	 * @param f
	 *            le fichier a charger
	 *@return dataClass un tableau de byte
	 */
	private byte[] loadClassData(File f) {
		byte[] dataClass = null;
		try {
			FileInputStream fileInputStream = new FileInputStream(f);
			int classSize = fileInputStream.available();
			dataClass = new byte[classSize];
			fileInputStream.read(dataClass);
			fileInputStream.close();
		} catch (FileNotFoundException e) {
//			System.out.println("!!! fichier introuvable");
		} catch (IOException e) {
//			System.out.println("!!! erreur de lecture");
		}
		return dataClass;
	}

	/**
	 * charge un fichier .class dans la machine virtuelle
	 * 
	 * @param f
	 *            le fichier a charger
	 *@param resolve
	 *            le boolean indiquant si la la Class est deja chargé ou non
	 *@return c Class
	 */
	public synchronized Class<?> loadClass(File f, boolean resolve) {
		Class<?> c = null;
		if (c == null) {
			c = (Class<?>) cache.get(f.getName());
			if (c == null) {
				try {
					data = loadClassData(f);
					c = defineClass(null, data, 0, data.length);
					cache.put(f.getName().replace(".class", ""), c);
				} catch (ClassFormatError e) {
//					System.out.println("class non valide");
				} catch (NullPointerException e) {
//					System.out.println("class non trouvée");
				} catch (Exception e) {
//					System.out
//							.println("y'a un truc qui passe pas avec cette class");
				} catch (NoClassDefFoundError e) {
//					System.out
//							.println("y'a un truc qui passe pas avec cette class");
				}
			}
			if (resolve) {
				resolveClass(c);
			}
		}
		// translate(c);
		return c;
	}

	/**
	 * lit et charge le byte-code d'un fichier .class dans l'attribut data
	 * 
	 * @param name
	 *            le nom du fichier a charger
	 *@return dataClass un tableau de byte
	 */
	private byte[] loadClassData(String name) {
		byte[] dataClass = null;
		try {
			FileInputStream fileInputStream = new FileInputStream(name);
			int classSize = fileInputStream.available();
			dataClass = new byte[classSize];
			fileInputStream.read(dataClass);
			fileInputStream.close();
		} catch (FileNotFoundException e) {
//			System.out.println("!!! fichier introuvable");
		} catch (IOException e) {
//			System.out.println("!!! erreur de lecture");
		}
		return dataClass;
	}

	/**
	 * charge un fichier .class dans la machine virtuelle
	 * 
	 * @param name
	 *            le nom du fichier a charger
	 *@param resolve
	 *            le boolean indiquant si la la Class est deja chargé ou non
	 *@return c Class
	 */
	public synchronized Class<?> loadClass(String name, boolean resolve) {
		Class<?> c = null;
		try {
			c = findSystemClass(name);
		} catch (ClassNotFoundException e) {
		}

		if (c == null) {
			name = name + ".class";
			c = (Class<?>) cache.get(name);
			if (c == null) {
				try {
					data = loadClassData(name);
					c = defineClass(null, data, 0, data.length);				
					cache.put(name, c);
				} catch (ClassFormatError e) {
//					System.out.println("class non valide");
				} catch (NullPointerException e) {
//					System.out.println("class non trouvée");
				} catch (Exception e) {
//					System.out
//							.println("y'a un truc qui passe pas avec cette class");
				} catch (NoClassDefFoundError e) {
//					System.out
//							.println("y'a un truc qui passe pas avec cette class");
				}
				catch (IllegalAccessError e) {
//					System.out
//					.println("y'a un truc qui passe pas avec cette class");
				}

			}
			if (resolve) {
				resolveClass(c);
			}
		}

		// translate(c);
		return c;
	}

	/**
	 * affiche des infos sur unc Class
	 * 
	 * @param c
	 *            la Class dont on veut les infos
	 */
	public void translate(Class<?> c) {
		System.out.println("class name : " + c.getName());
		System.out.println("Superclass name : " + c.getSuperclass());
		System.out.println("Number of methods : " + c.getMethods().length);
		System.out.println("Number of attributs : "
				+ c.getTypeParameters().length);

	}

	/**
	 * lance une classe chargé dans la machine virtuelle
	 * 
	 * @param nom
	 *            de la classe a lancer
	 */

	public void launch(String nom) {
		try {
			Class<?> classe;
			classe = loadClass(nom, true);
			System.out.println("classe chargee");
			Thread main = (Thread) (classe.newInstance());
			System.out.println("objet créé : " + main);
			main.start();

		} catch (IllegalAccessException e) {
			System.out.println("IllegalAccessException");
		} catch (InstantiationException e) {
			System.out.println("InstantiationException");
		}
	}

	public static void main(String args[]) {
		System.out.println("debut");
		System.out.println();
		ClassLoaderACL c = new ClassLoaderACL();
		try {
			// c.loadClass("/home/olivier/workACL/GenCocola/bin/serialisator/Serialisator");
			c.loadClass("/home/olivier/workACL/mon JDK/bin/time/Chrono");
			// c.launch("ClassLoaderACL");
			c.loadClass("/home/olivier/workACL/acl-infoCollect/bin/classFile/ClassFinder");
			 c.launch("/home/olivier/workACL/acl-infoCollect/bin/classFile/ClassFinder");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		// c.loadClass("/home/olivier/Bureau/test");
		// c.launch("javaFinder");
		System.out.println();
		System.out.println("fin");
	}
}
